/**
 * Copyright (C) 2016-2019 DSpot Sp. z o.o
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dspot.declex.override.handler;

import com.dspot.declex.action.Actions;
import com.dspot.declex.api.action.process.ActionInfo;
import com.dspot.declex.override.helper.DeclexAPTCodeModelHelper;
import com.dspot.declex.override.holder.AbilityActionHolder;
import com.dspot.declex.override.holder.FragmentActionHolder;
import com.dspot.declex.util.TypeUtils;
import com.helger.jcodemodel.AbstractJClass;
import com.helger.jcodemodel.JDefinedClass;
import com.helger.jcodemodel.JMethod;
import com.helger.jcodemodel.JMod;
import com.helger.jcodemodel.JVar;

import org.ohosannotations.ElementValidation;
import org.ohosannotations.OhosAnnotationsEnvironment;
import org.ohosannotations.helper.APTCodeModelHelper;
import org.ohosannotations.helper.InjectHelper;
import org.ohosannotations.holder.EAbilityHolder;
import org.ohosannotations.holder.HasIntentBuilder;
import org.ohosannotations.internal.core.helper.IntentBuilder;
import org.ohosannotations.internal.model.AnnotationElements;

import java.lang.reflect.Field;
import java.util.Set;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;

import static com.helger.jcodemodel.JExpr._this;
import static com.helger.jcodemodel.JExpr.invoke;
import static com.helger.jcodemodel.JExpr.ref;

/**
 * 额外的处理程序
 *
 * @author Administrator
 * @date 2021/07/27
 */
public class ExtraHandler extends org.ohosannotations.internal.core.handler.ExtraHandler {

    /**
     * 额外的处理程序
     *
     * @param environment 环境
     */
    public ExtraHandler(OhosAnnotationsEnvironment environment) {
        super(environment);

        codeModelHelper = new DeclexAPTCodeModelHelper(getEnvironment());
    }

    /**
     * 验证
     *
     * @param element 元素
     * @param validation 验证
     */
    @Override
    public void validate(Element element, ElementValidation validation) {

        super.validate(element, validation);
        if (!validation.isValid()) return;

        final AnnotationElements validatedElements = getEnvironment().getValidatedElements();

        if (validatedElements.isAncestor(element)) {

            Set<AnnotationElements.AnnotatedAndRootElements> subClasses = validatedElements.getAncestorSubClassesElements(element);
            for (AnnotationElements.AnnotatedAndRootElements subClass : subClasses) {

                if (getEnvironment().getValidatedElements().isAncestor(subClass.rootTypeElement)) continue;

                final String subClassName = subClass.rootTypeElement.asType().toString();
                final ActionInfo abilityActionInfo = Actions.getInstance().getActionInfos().get(subClassName + "ActionHolder");

                if (element.getKind() == ElementKind.PARAMETER) {
                    FragmentActionHolder.addFragmentArg(abilityActionInfo, element.getEnclosingElement(), getEnvironment());
                } else {
                    FragmentActionHolder.addFragmentArg(abilityActionInfo, element, getEnvironment());
                }

            }

        } else {

            final Element rootElement = TypeUtils.getRootElement(element);
            final String rootElementClass = rootElement.asType().toString();
            final ActionInfo abilityActionInfo = Actions.getInstance().getActionInfos().get(rootElementClass + "ActionHolder");

            if (element.getKind() == ElementKind.PARAMETER) {
                FragmentActionHolder.addFragmentArg(abilityActionInfo, element.getEnclosingElement(), getEnvironment());
            } else {
                FragmentActionHolder.addFragmentArg(abilityActionInfo, element, getEnvironment());
            }
        }

    }

    /**
     * 过程
     *
     * @param element 元素
     * @param holder 持有人
     */
    @Override
    public void process(Element element, EAbilityHolder holder) {

        try {

            APTCodeModelHelper helper = new DeclexAPTCodeModelHelper(getEnvironment());
            helper.getActualType(element, holder);

            IntentBuilder builder = ((HasIntentBuilder) holder).getIntentBuilder();

            Field helperField = IntentBuilder.class.getDeclaredField("codeModelHelper");
            helperField.setAccessible(true);
            helperField.set(builder, helper);

            helperField = InjectHelper.class.getDeclaredField("codeModelHelper");
            helperField.setAccessible(true);
            helperField.set(injectHelper, helper);

        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        }

        super.process(element, holder);

        if (element.getKind() != ElementKind.PARAMETER) {
            AbilityActionHolder actionHolder = holder.getPluginHolder(new AbilityActionHolder(holder));
            JDefinedClass AbilityAction = actionHolder.getAbilityAction();

            final String fieldName = element.getSimpleName().toString();

            final Element paramElement;
            if (element.getKind() == ElementKind.METHOD) {
                paramElement = ((ExecutableElement) element).getParameters().get(0);
            } else {
                paramElement = element;
            }

            final AbstractJClass clazz = codeModelHelper.elementTypeToJClass(paramElement);

            JMethod fieldMethod = AbilityAction.method(JMod.PUBLIC, AbilityAction, fieldName);
            JVar fieldMethodParam = fieldMethod.param(clazz, fieldName);
            fieldMethod.body().add(invoke(ref("builder"), fieldName).arg(fieldMethodParam));
            fieldMethod.body()._return(_this());
        }

    }
}
